package com.gmrz.uaf.protocol.v1.processor;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.uaf.common.*;
import com.gmrz.uaf.crypto.CertUtil;
import com.gmrz.uaf.crypto.KeyAttestation.KeyAttestationBcUtil;
import com.gmrz.uaf.crypto.sm.SM2Utils;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.DAOFactory;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.db.dao.DevicesDao;
import com.gmrz.uaf.policy.verification.PolicyVerificationException;
import com.gmrz.uaf.policy.verification.PolicyVerificationProcessor;
import com.gmrz.uaf.policy.verification.StepupPolicyProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.*;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.protocol.v1.validaton.ValidationException;
import com.gmrz.uaf.protocol.v1.validaton.Validator;
import com.gmrz.uaf.protocol.v1.validaton.reg.RegAssertionValidationException;
import com.gmrz.uaf.protocol.v1.validaton.reg.RegAssertionValidator;
import com.gmrz.uaf.protocol.v1.validaton.reg.RegResponseValidator;
import com.gmrz.uaf.server.ServerContext;
import com.gmrz.uaf.server.validation.MaxRegValidator;
import com.gmrz.uaf.servlet.v1.request.RegSendContext;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.util.Convert;
import com.gmrz.util.CryUtil;
import com.gmrz.util.UUIDGenerator;
import com.gmrz.util.db.DBUtil;
import com.google.common.base.Strings;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

public class UAFRegFinishProcessor extends UAFBaseProcessor {
    private static final Logger LOG = LogManager.getLogger(UAFRegFinishProcessor.class);


    protected DeregisterationRequest toDelete(String hashedUserName, String appID, String deviceID, String authType, String transType, Connection conn)
            throws DAOException {
        LOG.debug("toDelete:update last registration info's status from disabled(3) to deleted(2) by userName[{}],appID[{}],deviceID[{}],authType[{}],transType[{}]",
                hashedUserName, appID, deviceID, authType, transType);
        //查询状态为DISABLED(3)的注册信息,并生成注销报文
        Set<Authenticator> authenticators = getAuthenticatorsOnOneDevice(hashedUserName, appID, deviceID, authType, transType,
                Constants.AuthenticatorStatus.DISABLED.getStatus());

        DeregisterationRequest request = GuiceUtil.getProcessorInjector().getInstance(DeregisterationRequest.class);
        request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(appID,transType));
        List<DeregisterAuthenticator> deregList = new ArrayList<DeregisterAuthenticator>();

        Authenticator tmp = null;
        for (Authenticator authenticator : authenticators) {
            tmp = authenticator;
            DeregisterAuthenticator da = new DeregisterAuthenticator();
            da.setAaid(authenticator.getAAID());
            da.setKeyID(authenticator.getKeyID());
            deregList.add(da);
            request.setDeregisterAuthenticators(deregList);
            break;
        }
        //更新DISABLED(3)的注册信息为DELETED(2)
        if (tmp != null) {
            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
            adao.delete(tmp, Constants.AuthenticatorStatus.DELETED.getStatus());
        }
        if (serverConfig.getAppIdUrlBoolean(appID,transType)) {
            setFacetsAaid(request, appID);
        }
        return request;
    }

    public UAFRegFinishResultBean process(RegistrationResponse response, String hashedUserName, RegSendContext regSendContext)
            throws UAFException, ChallengeStoreException, RegistrationFailedException, SQLException {

        String appID = regSendContext.getAppID();
        String authType = regSendContext.getAuthType();
        String transType = regSendContext.getTransType();
        String opType = regSendContext.getOpType();
        DeviceDetails deviceDetails = regSendContext.getDevices();
        String mobile = regSendContext.getMobile();
        String rf1 = regSendContext.getRf1();
        String rf2 = regSendContext.getRf2();
        String ext = regSendContext.getExt();
        String custType = regSendContext.getCustType();
        String instCode = regSendContext.getInstCode();

        DeregisterationRequest deregisterationRequest = null;
        LOG.info("Validating registration response.");
        Validator<RegistrationResponse> validator = new RegResponseValidator(this.serverConfig,
                Constants.Operation.REG, appID,transType);

        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_2 && !ServerContext.getServerContext().isPerform()) {
            validator.validate(response);
        }
        LOG.info("Validation completed for registration response.");
        ServerData serverData = response.getOperationHeader().getServerData();
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_2 && !ServerContext.getServerContext().isPerform()) {
            /**
             * 检测重放攻击；
             * 重放攻击：是指攻击者发送一个目的主机已接收过的包，来达到欺骗系统的目的，主要用于身份认证过程，破坏认证的正确性。
             * 重放攻击可以由发起者，也可以由拦截并重发该数据的敌方进行。
             */
            checkForReplayAttack(serverData);
        }
        List<Extension> exts = response.getOperationHeader().getExtensions();
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_2 && !ServerContext.getServerContext().isPerform()) {
            // 扩展执行
            processExtensions(exts);
        }

        TLSData tlsData = response.getFinalChallengeParams().getTLSData();
        processTLSData(tlsData);

        String policyName = serverData.getPolicyName();//policy_appID_authType
        String custNo = serverData.getUserName();//客户号

        List<RegistrationAssertion> assertions = response.getAssertions();
        if ((assertions == null) || (assertions.isEmpty())) {
            String msg = "No assertion found in the registration response for user[" + custNo + "], policy["
                    + policyName + "].";
            logAndThrowRFE(msg);
        }
        Connection conn = null;
        List<Authenticator> successAuthenticators = new ArrayList<Authenticator>();
        try {

            Map<String, AuthenticatorSpec> specMap = new HashMap<String, AuthenticatorSpec>();

            for (RegistrationAssertion regAssertion : assertions) {
                String s = Convert.toBase64(regAssertion.getAuthenticatorResponse().getKRD().getUvi());
                LOG.debug("uvi:" + s);
                Authenticator token = null;
                String assertionAAID = null;
                AuthenticatorSpec spec = null;
                //try {
                    assertionAAID = regAssertion.getAuthenticatorResponse().getKRD().getAAID();
                    spec = assertionAAID == null ? null : getAuthenticatorSpec(assertionAAID);
                    if (spec == null) {
                        LOG.error("Could not locate metadata for AAID[{}]", assertionAAID);
                        throw new MetadataNotFoundException(UAFErrorCode.DB_QUERY_METADATA_FAILED,
                                "Could not locate metadata for AAID[" + assertionAAID + "]");
                    }
                    // 处理assertion断言，在断言中获取aaid、keyid、pubkey公钥等
                    token = processAssertion(response, regAssertion, spec, hashedUserName, custNo, appID,
                            deviceDetails.getDeviceID(), authType, transType, ext,regSendContext.getRegFrom());
                    token.setMobile(mobile);
                    token.setRf1(rf1);
                    token.setRf2(rf2);
                if (authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) < 0) {

                    boolean issoft = false;
                    String AndroidKey = "TEE";
                    if (null != spec.getSupportedExtensions() && !spec.getSupportedExtensions().isEmpty()) {
                        if (null != regAssertion.getExtensions() && !regAssertion.getExtensions().isEmpty()) {
                            if (AndroidKey.equals("SOFTWARE")) {
                                issoft = true;
                            }
                            // goole keystore方案的证书链校验
                            processAndroidKeyAttestation(spec, regAssertion, response.getFinalChallengeParams().getServerChallenge().getChallenge(), response.getFCPBase64(), issoft);
                        } else {
                            String msg = "No Keystore assertions exts found in the response for user["
                                    + hashedUserName + "], policy[" + policyName + "].";
                            logAndThrowRFE(msg);
                        }
                    }
                }
//                } catch (Exception ex) {
//                    LOG.error("Registration assertion failed for AAID[{}]", assertionAAID, ex);
//                    continue;
//                }

                if (token != null) {
                    successAuthenticators.add(token);
                    specMap.put(assertionAAID, spec);
                }

            }
            if ((successAuthenticators != null) && (!successAuthenticators.isEmpty())) {

                Policy policy = this.policyManager.getNamedPolicy(authType, appID, transType);
                verifyRegistrationPolicy(successAuthenticators, assertions, specMap, policy, hashedUserName, appID
                        , deviceDetails.getDeviceID(), authType, transType);
                conn = UAFDBConnectionMgr.getConnection(false);
                try {
                    // 单用户可注册的设备上线
                    verifyMaxAuthenticatorsLimit(successAuthenticators.size(), hashedUserName, appID, conn,authType);
                } catch (RegistrationFailedException e) {
                    throw e;
                }
                for (Authenticator authenticator : successAuthenticators) {
                    LOG.info("Saving successfully policy verified authenticator with AAID : {}",
                            authenticator.getAAID());
                    AuthenticatorSpec spec = specMap.get(authenticator.getAAID().format());
                    authenticator.setCustType(custType);
                    authenticator.setInstCode(instCode);
                    // 保存注册的认证数据
                    saveAuthenticator(authenticator, spec, opType, conn);
                    deviceDetails.setAppID(appID);
                    deviceDetails.setUserName(hashedUserName);
                    deviceDetails.setAuthType(authType);
                    deviceDetails.setTransType(transType);
                    deviceDetails.setStatus(Constants.AuthenticatorStatus.ACTIVE.getStatus());
                    // 保存设备信息
                    saveDevice(deviceDetails, opType, conn);
                }
                deregisterationRequest = toDelete(hashedUserName, appID, deviceDetails.getDeviceID(), authType, transType, conn);
                for (Authenticator authenticator : successAuthenticators) {
                    // 保存证书数据
                    saveCertBean(authenticator, conn);
                }
                conn.commit();
                for (Authenticator authenticator : successAuthenticators) {
                    // 更新缓存中的已注册使用的证书数量
                    updateCertNumCache(authenticator);
                }
                if (serverConfig.getAppIdUrlBoolean(appID,transType) && null != deregisterationRequest) {
                    setFacetsAaid(deregisterationRequest, appID);
                }
            } else {
                String msg = "Failed to validate [" + assertions.size() + "] registration assertion(s).";
                logAndThrowRFE(msg);
            }
            UAFRegFinishResultBean uafRegFinishResultBean = getResultBean(deregisterationRequest, successAuthenticators);
            return uafRegFinishResultBean;

        } catch (SQLException sqle) {
            if(null != conn) {
                conn.rollback();
            }
            LOG.error("Failed to  connect to database, ", sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR,
                    "Failed to connect to database", sqle);
        } catch (UAFException e) {
            if (null != conn) {
                conn.rollback();
            }
            throw e;
        }catch (UAFCommonRuntimeException e) {
            if(null != conn) {
                conn.rollback();
            }
            throw e;
        } catch (Exception e) {
            if(null != conn) {
                conn.rollback();
            }
            LOG.error(e);
            throw new RegistrationFailedException(e);
        } finally {
            DBUtil.close(conn);
        }
    }

    protected void updateCertNumCache(Authenticator authenticator) throws ChallengeStoreException {
    }

    protected void saveCertBean(Authenticator authenticator, Connection conn) throws DAOException, ChallengeStoreException, UAFException, SQLException {
    }


    protected UAFRegFinishResultBean getResultBean(DeregisterationRequest deregisterationRequest, List<Authenticator> successAuthenticators) {
        UAFRegFinishResultBean uafRegFinishResultBean = new UAFRegFinishResultBean();
        uafRegFinishResultBean.setDeregisterationRequest(deregisterationRequest);
        return uafRegFinishResultBean;
    }

    private void processAndroidKeyAttestation(AuthenticatorSpec spec, RegistrationAssertion regAssertion, byte[] challenge, String fcp, boolean issoft) throws PolicyVerificationException {
        String rootcert = null;
        String ResponseCert = null;
        String certstr = null;

        // 从元数据中获取的"支持扩展"的证书
        List<Extension> metaext = spec.getSupportedExtensions();
        for (Extension extension : metaext) {
            rootcert = extension.getData();
        }
        JsonParser parser = new JsonParser();
        JsonObject jsonObject = parser.parse(rootcert).getAsJsonObject();
        Certificate AndroidKeyrootCert;
        List<X509Certificate> certList = new ArrayList<X509Certificate>();
        try {
            JsonArray jsonArray = jsonObject.getAsJsonArray("attestationRootCertificates");
            if (null != jsonArray && jsonArray.size() > 0) {
                for (int i = 0; i < jsonArray.size(); i++) {
                    certstr = jsonArray.get(i).getAsString();
                    AndroidKeyrootCert = cryptoEngine.getAttestationCertHandler().decode(Convert.fromBase64(certstr));
                    X509Certificate csCert = (X509Certificate) AndroidKeyrootCert;
                    certList.add(csCert);
                }
            }
        } catch (Exception e) {
            LOG.error("supportedExtensions attestationRootCertificates get cert error");
        }
        X509Certificate[] rootArray = new X509Certificate[certList.size()];
        rootArray = certList.toArray(rootArray);


        // 从注册完成请求报文中获取的证书
        for (Extension regext : regAssertion.getExtensions()) {
            if (!regext.getId().equals("fido.uaf.android.key_attestation")) {
                LOG.error("Extension ID value is invalid");
                throw new PolicyVerificationException(UAFErrorCode.ERROR_KEYSTORE_CERTCHAIN_VALIDATION_FAILED);
            }
            if (regext.getData().equals("p") || regext.getData().equals("a")) {
                LOG.error("Extension Data value is invalid");
                throw new PolicyVerificationException(UAFErrorCode.ERROR_KEYSTORE_CERTCHAIN_VALIDATION_FAILED);
            }
            ResponseCert = regext.getData();
        }
        JsonArray RespJarray = parser.parse(ResponseCert).getAsJsonArray();
        Certificate AndroidKeyRespCert;
        List<X509Certificate> RespcertList = new ArrayList<X509Certificate>();
        try {
            if (null != RespJarray && RespJarray.size() > 0) {
                for (int i = 0; i < RespJarray.size(); i++) {
                    certstr = RespJarray.get(i).getAsString();
                    AndroidKeyRespCert = cryptoEngine.getAttestationCertHandler().decode(Convert.fromBase64(certstr));
                    X509Certificate csCert = (X509Certificate) AndroidKeyRespCert;
                    RespcertList.add(csCert);
                }
            }
        } catch (Exception e) {
            LOG.error("ResponseCert get cert error");
        }
        X509Certificate[] RespcertArray = new X509Certificate[RespcertList.size()];
        RespcertArray = RespcertList.toArray(RespcertArray);

        byte[] pubkey = regAssertion.getAuthenticatorResponse().getKRD().getPubKey();
        byte[] Hashchallenge = regAssertion.getAuthenticatorResponse().getKRD().getHashedFinalChallenge();

        boolean verifyvalue = KeyAttestationBcUtil.verify(RespcertArray, rootArray, Hashchallenge, pubkey, issoft);
        if (!verifyvalue) {
            LOG.error("Failed to Certificate chain validation is missing");
            throw new PolicyVerificationException(UAFErrorCode.ERROR_KEYSTORE_CERTCHAIN_VALIDATION_FAILED);
        }
    }


    protected Authenticator processAssertion(RegistrationResponse registrationResponse,
                                             RegistrationAssertion registrationAssertion, AuthenticatorSpec spec,
                                             String hashedUserName, String custNo, String appID,
                                             String deviceID, String authType, String transType, String ext,String from) throws ValidationException,
            DAOException, PolicyVerificationException, MetadataNotFoundException, TLVParsingException,
            CertificateValidationException, SignatureValidationException, IOException, ChallengeStoreException, PluginException, CertificateException, SignatureException, NoSuchAlgorithmException {
        LOG.info("Validating Registration Assertion.");
        RegAssertionValidator validator = new RegAssertionValidator(spec, registrationResponse.getFCPBase64());
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_3 && !ServerContext.getServerContext().isPerform()) {
            validator.validate(registrationAssertion);
        }
        LOG.info("Validation completed for Registration Assertion.");

        AAID aaid = spec.getAAID();
        LOG.info("AAID[{}]", aaid);
        String assertionScheme = registrationAssertion.getKeyRegistrationDataScheme();
        LOG.info("Assertion Scheme[{}]", assertionScheme);

        ASMRegResponse asmRegResponse = registrationAssertion.getAuthenticatorResponse();
        KeyRegistrationData krd = asmRegResponse.getKRD();
        // 获取证书，这个证书是设备公钥证书
        List<Certificate> attestationCerts = asmRegResponse.getAttestationCerts();
        // 获取从认证器中的ASM返回的响应中的数字签名
        byte[] signature = asmRegResponse.getSignature();

        byte[] krdBytes = krd.getReceivedKRDBytes();
        TLVType2 TLVType2 = new TLVType2() {
        };
        TLVType2.addTLVSegment(Constants.UAFV1AssertionTag.TAG_UAFV1_KRD.getTagValue(), krdBytes);
        byte[] signedKRDBytes = TLVType2.getTLVBytes();

        String msg = "";
        if (this.getServerConfig().getPerformModel() < Constants.PERFORM_MODEL_LEVEL_3 && !ServerContext.getServerContext().isPerform()) {
            if (asmRegResponse.getAttestationType().equals(Constants.AttestationType.ATTESTATION_BASIC_FULL)) {
                if ((spec.getRootCertificates() != null) && (!spec.getRootCertificates().isEmpty())) {

                    //TODO
                    if (!"0011#0701".equals(aaid.format())) {
                        // 校验证书
                        if(spec.getAlgorithm() == 7){
                            boolean signFlag = CertUtil.validateSecondLevelCertificate(attestationCerts.get(0).getEncoded(),Convert.fromBase64(spec.getAttestationRootCertificatesStr()));
                            if (!signFlag) {
                                throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED);
                            }
                        }else {
                            validateCertificate(attestationCerts, spec, aaid.format());
                        }
                    }
                    if(spec.getAlgorithm() == 7){
                        boolean signFlag = SM2Utils.verifySignRaw(CertUtil.getPublicKey(attestationCerts.get(0).getEncoded()), signedKRDBytes, signature);
                        if (!signFlag) {
                            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED);
                        }
                    }else {
                        LOG.info("Successfully validated the attestation certificate for AAID[" + aaid + "]");
                        int signAlg = krd.getSignAlgorithm();
                        LOG.info("Reg signalg value" + signAlg);
                        //加密算法套件。国密
                        if (krd.getSignAlgorithm() == 7) {
                            signAlg = 1;
                        }
                        // 校验签名
                        validateSignature(signAlg, attestationCerts.get(0), signedKRDBytes, signature);
                        LOG.info("Successfully validated the attestation signature for AAID[" + aaid + "]");
                    }
                } else {
                    msg = "Attestation verification failed, root certificate not found in metadata for ["
                            + asmRegResponse.getAttestationType().getName() + "]";

                    logAndThrowRFE(msg);
                }
            } else if (asmRegResponse.getAttestationType().equals(Constants.AttestationType.ATTESTATION_BASIC_SURROGATE)) {
                // 公司keystore方案、goole keystoe方案的处理实现
                if ((spec.getRootCertificates() == null) || (spec.getRootCertificates().isEmpty())) {
                    byte[] pubKey = krd.getPubKey();
                    if(spec.getAlgorithm() == 7){
                        boolean signFlag = SM2Utils.verifySignRaw(pubKey, signedKRDBytes, signature);
                        if (!signFlag) {
                            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED);
                        }
                    }else {
                        validateSignature(krd.getKeyFormat(), pubKey, krd.getSignAlgorithm(), signedKRDBytes, signature);
                    }
                    LOG.info("Successfully validated the attestation signature for AAID[" + aaid + "]");
                } else {
                    msg = "Surrogate Basic Attestation is not supported when metadata has root certificate for AAID["
                            + spec.getAAID() + "]";

                    logAndThrowRFE(msg);
                }
            } else {
                msg = "AttestationType[" + asmRegResponse.getAttestationType() + "] not supported";
                logAndThrowRFE(msg);
            }
            LOG.info("Attestation Signature validated successfully for AAID[{}], and username[{}]",
                    aaid, custNo);
        }
////////
        List<Extension> exts = registrationAssertion.getExtensions();
        if ((exts != null) && (!exts.isEmpty())) {
            processExtensions(exts);
        }
        Authenticator retval = new Authenticator().withAAID(aaid).withKeyID(new KeyID().withId(krd.getKeyID()))
                .withKeyFormat(krd.getKeyFormat()).withAuthenticatorVersion(krd.getAuthenticatorVersion());
        retval.setID(UUIDGenerator.generateID());
        retval.setAppID(appID);
        retval.setDeviceID(deviceID);
        retval.setAuthType(authType);
        retval.setTransType(transType);
        retval.setPubKey(Convert.toBase64(krd.getPubKey()));
        retval.setStatus(registrationResponse.getRegistrationStatus().getStatus());
        retval.setCustNo(custNo);
        retval.setUserName(hashedUserName);
        retval.setMetadata(spec);
        retval.setRegCounter(krd.getRegisterCounter());
        retval.setFrom(from);
        List<PNGCharacteristics> firstPngCharacteristics = new ArrayList<PNGCharacteristics>();
        List<PNGCharacteristics> pngCharacteristics = registrationAssertion.getTcDisplayPNGCharacteristics();
        if (null != pngCharacteristics && pngCharacteristics.size() > 0) {
            firstPngCharacteristics.add(pngCharacteristics.get(0));
        }
        retval.setPngCharacteristics(firstPngCharacteristics);
        byte[] uviBytes = krd.getUvi();
        if (authType.equals(Constants.AuthType.GESTUREPRINT.getAuthType())) {
            if (null == uviBytes) {
                throw new RegAssertionValidationException(UAFErrorCode.PROTOCOL_UVI_NOT_NONE);
            }
        }
        if ((uviBytes != null) && (uviBytes.length > 0)) {
            if (authType.equals(Constants.AuthType.GESTUREPRINT.getAuthType())) {
                byte[] keys = Arrays.copyOf(CryUtil.getSHA256(Convert.toBase64(registrationResponse.getOperationHeader().getServerData().getChallenge()).getBytes()), 16);
                byte[] aes256Decode = CryUtil.Aes256Decode(uviBytes, keys);
                retval.setUvi(Convert.toBase64(aes256Decode));
            } else {
                retval.setUvi(Convert.toBase64(uviBytes));
            }
            List<String> base64UvsList = new ArrayList<String>();
            base64UvsList.add(Convert.toBase64(uviBytes));
            retval.setUvs(base64UvsList);
        }
//        List<byte[]> uvsList = krd.getUvs();
//        if (uvsList != null && uvsList.size() > 0) {
//            List<String> base64UvsList = new ArrayList<String>();
//            for (byte[] uvi : uvsList) {
//                base64UvsList.add(Convert.toBase64(uvi));
//            }
//            retval.setUvs(base64UvsList);
//        }
        return retval;
    }

    protected Authenticator saveAuthenticator(Authenticator a, AuthenticatorSpec spec, String opType,
                                              Connection conn) throws DAOException, ChallengeStoreException, UAFException {
        if (spec == null) {
            LOG.error("Authenticator Spec cannot be NULL for AAID[{}]", a.getAAID());
            throw new RegistrationFailedException("Authenticator Spec cannot be NULL for AAID[" + a.getAAID() + "]");
        }
        if (!a.getAAID().format().equalsIgnoreCase(spec.getAAID().format())) {
            throw new RegistrationFailedException(
                    "Illegal state, AAID mismatch. Cannot save the assertion with AAID: " + a.getAAID().format());
        }

        a.setMetadata(spec);
        AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
        if (Constants.OPType.REGIST_SIGNLE.getOpType().equals(opType)) {
            if(a.getAuthType().compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0){
                Set<Authenticator> authenticators = adao.findUserNoDevice(a.getUserName(), a.getAppID(), a.getAuthType(), a.getTransType(), Constants.AuthenticatorStatus.ACTIVE.getStatus());
                if(null != authenticators){
                    UAFDeregisterProcessor uafDeregisterProcessor = GuiceUtil.getProcessorInjector().getInstance(UAFDeregisterProcessor.class);
                    uafDeregisterProcessor.processDereg(authenticators, a.getDeviceID(), "01",a.getAppID(),Constants.CANCEL_REASON_TYPE_6);
                }
            }else {
                adao.updateOther(a.getUserName(), a.getAppID(), a.getAuthType(), a.getTransType());
            }

        }else if (Constants.OPType.REGIST_RESPONSE_NO_KEYID.getOpType().equals(opType)) {
            adao.updateOtherByDeviceID(a.getUserName(), a.getAppID(), a.getAuthType(), a.getTransType(),a.getDeviceID());
        }
        adao.insert(a);
        return a;
    }

    protected void saveDevice(DeviceDetails a, String opType, Connection conn) throws DAOException {

        DevicesDao devdao = UAFDAOFactory.createDevicesDAO(conn);
        if (Constants.OPType.REGIST_SIGNLE.getOpType().equals(opType)) {
            devdao.updateOther(a.getUserName(), a.getAppID(), a.getAuthType(), a.getTransType());
        }
        DeviceDetails deviceDetails = devdao.findOne(a.getAppID(), a.getUserName(), a.getDeviceID(), a.getAuthType(), a.getTransType());
        if (deviceDetails != null) {
            deviceDetails.setStatus(Constants.AuthenticatorStatus.ACTIVE.getStatus());
            devdao.update(deviceDetails);
        } else {
            devdao.insert(a);
        }

    }


    protected void verifyRegistrationPolicy(List<Authenticator> retval, List<RegistrationAssertion> assertions,
                                            Map<String, AuthenticatorSpec> specMap, Policy p, String username, String appID, String deviceID,
                                            String authType, String transType)
            throws PolicyVerificationException, DAOException {
        List<MatchCriteria> mcList = getMCListForAlreadyRegistered(username, appID, deviceID, authType, transType);
        if (mcList != null) {
            LOG.info("There are existing registration for the user : {}", username);
            p.appendDisallowed(mcList);
        }
        List<RegistrationAssertion> policyVerifiedAssertions = PolicyVerificationProcessor
                .verifyRegistrationPolicy(assertions, specMap, p);

        if (policyVerifiedAssertions == null) {
            LOG.error("No assertions were matched against the policy[{}]", p.getPolicyName());
            throw new PolicyVerificationException(UAFErrorCode.PROTOCOL_POLICY_VALIDATION_FAILED);
        }
        List<String> verifiedAAIDs = new ArrayList<String>();
        for (RegistrationAssertion assertion : policyVerifiedAssertions) {
            String aaid = assertion.getAuthenticatorResponse().getKRD().getAAID();
            verifiedAAIDs.add(aaid);
        }
        for (Iterator<Authenticator> authenticatorIter = retval.iterator(); authenticatorIter.hasNext(); ) {
            Authenticator authenticator = authenticatorIter.next();
            if (verifiedAAIDs.contains(authenticator.getAAID().format())) {
                LOG.info("Success in policy verification for AAID : {}",
                        authenticator.getAAID().format());
            } else {
                authenticatorIter.remove();
                LOG.error("Failed in policy verification for AAID : {}.Removing from the list.",
                        authenticator.getAAID().format());
            }
        }
    }

    protected void logAndThrowRFE(String msg) {
        LOG.error(msg);
        throw new RegistrationFailedException(msg);
    }

    protected List<MatchCriteria> getMCListForAlreadyRegistered(String username, String appID, String deviceID,
                                                                String authType, String transType) throws DAOException {
        LOG.info("getMCListForAlreadyRegistered [username=" + username);
        Set<Authenticator> registeredList = getAuthenticatorListForAlreadyRegistered(username, appID,
                deviceID, authType, transType);
        return StepupPolicyProcessor.convertAuthenticatorsToMCs(registeredList);
    }

    protected void verifyMaxAuthenticatorsLimit(int toBeRegisteredAuthenticatorsCount, String username, String tenantid, Connection conn,String authType)
            throws DAOException, ChallengeStoreException {
        if (Strings.isNullOrEmpty(username)) {
            LOG.error("username can not be empty for registration operation.");
            throw new RegistrationFailedException("username can not be empty for registration operation.");
        }
        AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
        int regAuthenticatorCount = adao.getActiveRegCount(username, tenantid);
        // 获取单用户可注册设备的上线，此处是在系统启动时进行数据库查询并通过guice注入给了MaxRegAuthenticatorsCount属性
        int registeredAuthenticatorsLimit = getServerConfig().getMaxRegAuthenticatorsCount();
        // 获取单用户可注册设备的上线，缓存查询，缓存不存在，则数据库查询
        //int registeredAuthenticatorsLimit = Integer.parseInt(this.getServerConfig().getUasConfig(Constants.UAF_REG_USER_MAX_AUTHENTICATORS));

        MaxRegValidator maxRegValidator = new MaxRegValidator();
        maxRegValidator.validateAuthenticatorsCount(regAuthenticatorCount, toBeRegisteredAuthenticatorsCount,
                registeredAuthenticatorsLimit, username);
    }

    public List<DeregisterAuthenticator> deleteLastOne(String userName,String appID,String deviceID,String authType,String transType,Authenticator auth) throws DAOException, ChallengeStoreException, AuthenticatorNotRegisteredException, SQLException {
        String value = this.getServerConfig().getUasConfig(Constants.UAF_DELETE_LAST_AUTHENTICATOR+authType);
        if(com.gmrz.util.Strings.isNotBlank(value)){
            Integer v = Integer.parseInt(value);
            if(v > 0){
                Connection conn = null;
                try {
                    conn = UAFDBConnectionMgr.getConnection();
                    AuthenticatorDAO dao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class).createAuthenticatorDAO(conn);

                    Set<Authenticator> authenticators = dao.findUserNoDeviceDer( userName,  appID,  authType,  transType,  Constants.AuthenticatorStatus.ACTIVE.getStatus());
                    if(null != authenticators && !authenticators.isEmpty() && authenticators.size() > v){
                        List<Authenticator> authList = new ArrayList<Authenticator>();
                        authList.addAll(authenticators);
                        //实现Comparator进行排序
                        boolean flag = false;
                        if (authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) >= 0) {
                            String notActivityValue = this.getServerConfig().getUasConfig(Constants.UAF_NOT_ACTIVITY_AUTHENTICATOR);
                            if(com.gmrz.util.Strings.isNotBlank(notActivityValue)) {
                                Integer notActivityV = Integer.parseInt(notActivityValue);
                                if (notActivityV > 0) {
                                    flag = true;
                                }
                            }
                        }else {
                            flag = true;
                        }

                        if(flag){
                            Collections.sort(authList,new Comparator<Authenticator>(){
                                @Override
                                public int compare(Authenticator o1, Authenticator o2) {
                                    return o1.getCreateTimeStamp().compareTo(o2.getCreateTimeStamp());
                                }
                            });
                        }else {
                            Collections.sort(authList, new Comparator<Authenticator>() {
                                @Override
                                public int compare(Authenticator o1, Authenticator o2) {
                                    if (o1.getCertificateExtendBean().getCertStatus() != o2.getCertificateExtendBean().getCertStatus()) {
                                        if (o1.getCertificateExtendBean().getCertStatus() == Constants.CERT_STATUS_3) {
                                            return 1;
                                        }
                                        if (o2.getCertificateExtendBean().getCertStatus() == Constants.CERT_STATUS_3) {
                                            return -1;
                                        }
                                        return o1.getCreateTimeStamp().compareTo(o2.getCreateTimeStamp());
                                    }
                                    return o1.getCreateTimeStamp().compareTo(o2.getCreateTimeStamp());
                                }
                            });
                        }

                        List<DeregisterAuthenticator> resultList = new ArrayList<DeregisterAuthenticator>();
                        Set<Authenticator> deleteSet = new HashSet<Authenticator>();
                        int n = authList.size() - v;
                        for(int i =0; i < n; i++) {
                            Authenticator authenticator = authList.get(i);
                            DeregisterAuthenticator da = new DeregisterAuthenticator();
                            da.setAaid(authenticator.getAAID());
                            da.setKeyID(authenticator.getKeyID());
                            da.setCustNo(authenticator.getCustNo());
                            resultList.add(da);
                            deleteSet.add(authenticator);
                        }
                        UAFDeregisterProcessor uafDeregisterProcessor = GuiceUtil.getProcessorInjector().getInstance(UAFDeregisterProcessor.class);
                        uafDeregisterProcessor.processDereg(deleteSet,  deviceID, "02", appID, Constants.CANCEL_REASON_TYPE_3);

                        return resultList;
                    }
                }finally {
                    DBUtil.close(conn);
                }
            }
        }
        return null;

    }

    public List<DeregisterAuthenticator> deleteOtherUser(String userName,String appID,String deviceID,String authType,String transType,Authenticator auth) throws DAOException, ChallengeStoreException, AuthenticatorNotRegisteredException, SQLException {
        String value = this.getServerConfig().getUasConfig(Constants.UAF_DELETE_OTHER_AUTHENTICATOR+authType);
        if(com.gmrz.util.Strings.isNotBlank(value)){
            Integer v = Integer.parseInt(value);
            if(v > 0){
                Connection conn = null;
                try {
                    conn = UAFDBConnectionMgr.getConnection();
                    AuthenticatorDAO dao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class).createAuthenticatorDAO(conn);

                    Set<Authenticator> authenticators = dao.findUserByDeviceIDAndAppID(deviceID,appID,  authType,  transType,  Constants.AuthenticatorStatus.ACTIVE.getStatus());
                    if(null != authenticators && !authenticators.isEmpty() && authenticators.size() > v){
                        List<Authenticator> authList = new ArrayList<Authenticator>();
                        authList.addAll(authenticators);
                        //实现Comparator进行排序
                        Collections.sort(authList,new Comparator<Authenticator>(){
                            @Override
                            public int compare(Authenticator o1, Authenticator o2) {
                                return o1.getCreateTimeStamp().compareTo(o2.getCreateTimeStamp());
                            }
                        });
                        LOG.debug("list:"+authList);
                        List<DeregisterAuthenticator> resultList = new ArrayList<DeregisterAuthenticator>();
                        Set<Authenticator> deleteSet = new HashSet<Authenticator>();
                        int n = authList.size() - v;
                        for(int i =0; i < n; i++) {
                            Authenticator authenticator = authList.get(i);
                            DeregisterAuthenticator da = new DeregisterAuthenticator();
                            da.setAaid(authenticator.getAAID());
                            da.setKeyID(authenticator.getKeyID());
                            da.setCustNo(authenticator.getCustNo());
                            resultList.add(da);
                            deleteSet.add(authenticator);
                        }
                        UAFDeregisterProcessor uafDeregisterProcessor = GuiceUtil.getProcessorInjector().getInstance(UAFDeregisterProcessor.class);
                        uafDeregisterProcessor.processDereg(deleteSet,  deviceID, "02", appID, Constants.CANCEL_REASON_TYPE_4);
                        return resultList;
                    }
                }finally {
                    DBUtil.close(conn);
                }
            }
        }
        return null;

    }

}
